"""11BS集中安装架测试台项目模块"""
from openpyxl.reader.excel import load_workbook
from openpyxl.styles import PatternFill

"""
模块功能：
    1. 囊括了11BS的测试定义，规定了每一个显示的channel在屏幕上显示的额外信息
    2. 对已经经过JSON协议器解析后的数据再按照11BS模块的数据进行解析对比，并以列表的形式返回错误的信息
    3. 在此模块中，将11BS的协议解析函数封装成了一个类，方便调用
"""

from screen.LogControl.log_init import *
from screen.ClassRefactoring.object_refactoring import HoverInfoManager
from openpyxl.styles import PatternFill
from openpyxl.styles.colors import Color
from colorsys import hsv_to_rgb

"""11BS集中安装架测试台项目模块的协议类"""


class Protocol_11BS:
    def __init__(self, main_window):
        self.main_window = main_window

        # 日志配置
        self.log = Log_Config()
        # 默认值
        self.__Default_value = '1' * 373

    """11BS数据解包函数"""

    def protocol_11bs_unpack(self, recv_data: bytes):
        """
        11BS协议解析函数
        :param recv_data:待解析的函数
        :return:状态码或者解析后的数据(数据只会将错误的数据返回)
        """
        # 判断功能ID
        if recv_data[0] != 0x02:
            self.log.logger.error("功能ID错误")
            return None

        # 获取有效数据长度
        length = recv_data[1]

        # 获取有效数据
        data = recv_data[2:length + 2]

        #将数据转换为二进制数据
        # {0:08b} 是一个格式化字符串，其中0是占位符，表示我们要插入的值（这里是byte）
        # :08b是格式规范，表示输出一个二进制表示，总宽度至少为8位，不够前面补0。
        binary_str_parts = ['{0:08b}'.format(byte) for byte in data]

        #将数据转换为字符串
        binary_str = ''.join(binary_str_parts)

        return self.__protocol_11bs_compare(binary_str)

    """数据对比函数"""

    def __protocol_11bs_compare(self, recv_data: str):
        """
        11BS协议数据对比函数， 比较两个长度相同的二进制数据，记录不匹配的位索引。
        :param recv_data:待比较的未知数据，二进制形式，长度也为373位。
        :return:不匹配位的索引列表。
        """
        try:
            # 判断长度
            assert len(recv_data) - 3 == len(self.__Default_value), "11BS模块对比的数据长度不一致"
            assert len(recv_data) >= 373, "11BS模块对比的数据长度不足373位"

            # 按位对比并返回不同的索引值
            # zip函数是将两个字符串配对，形成一个迭代器。迭代器中的每一个元素都是一对字符，分别对应两个字符串中的相同位置的字符，比如(r1,u1),(r2,u2),(r3,u3)
            # enumerate函数将迭代器中的每一个元素转化为一个元组，元组的第一个元素是索引，第二个元素是迭代器中的元素。比如(0,(r1,u1)),(1,(r2,u2)),(2,(r3,u3))
            mismatch_indices = [i + 1 for i, (rd, ud) in enumerate(zip(self.__Default_value, recv_data)) if rd != ud]

            return mismatch_indices

        except AssertionError as e:
            self.log.logger.error(e)
            return None

    def _11bs_initialize_hover_info(self):
        """
        在UI初始化完成后设置悬停信息
        """
        self.hoverManager = HoverInfoManager()
        index = 0
        #循环绑定通道中文信息
        for i in range(1, 374):
            index = min((i - 1) // 128 + 1, 3)  # i从1开始，因此减1再除以128，加1得到index，同时限制最大为3
            second_index = 128 if i in (128, 256) else i % 128
            channel_name = f'channel_{index}_{second_index}'
            channel_widget = getattr(self.main_window, channel_name, None)
            if channel_widget:
                self.hoverManager.add_hover_info(channel_widget, self.__get_channel_name(i))

    """获取通道中文名称"""

    def __get_channel_name(self, channel_number: int) -> str:
        """
        获取通道中文名称
        :param channel_number:通道编号
        :return:通道中文名称
        """
        #373个通道元素：参考11BS模块的通道测试定义
        self.Chinese = {1: "地勤话筒+", 2: "地勤话筒-", 3: "屏蔽地", 4: "短波电台收入+", 5: "短波电台收入-",
                        6: "屏蔽地", 7: "塔康导航音+",
                        8: "塔康导航音-", 9: "屏蔽地", 10: "MLS导航音+", 11: "MLS导航音-", 12: "屏蔽地",
                        13: "MLS导航音+", 14: "MLS导航音-",
                        15: "屏蔽地", 16: "JIDS话音2收 +", 17: "JIDS话音2收 -", 18: "屏蔽地", 19: "导弹告警+",
                        20: "导弹告警-", 21: "屏蔽地",
                        22: "地勤耳机+", 23: "地勤耳机-", 24: "屏蔽地", 25: "短波电台发+", 26: "短波电台发-",
                        27: "屏蔽地", 28: "话台收明话+",
                        29: "话台收明话-", 30: "屏蔽地", 31: "话台发明话+", 32: "话台发明话-", 33: "屏蔽地",
                        34: "副耳出+", 35: "副耳出-",
                        36: "屏蔽地", 37: "后舱串口收+", 38: "后舱串口收-", 39: "屏蔽地", 40: "后舱串口发+",
                        41: "后舱串口发-", 42: "屏蔽地",
                        43: "罗盘导航音+", 44: "罗盘导航音-", 45: "屏蔽地", 46: "罗盘导航音+", 47: "罗盘导航音-",
                        48: "屏蔽地",
                        49: "M_JIDS_PTT2", 50: "V_JIDS_PTT2", 51: "M_JIDS_PTT1", 52: "V_JIDS_PTT1", 53: "M_S_PTT",
                        54: "M_S_PTT", 55: "V_S_PTT",
                        56: "V_S_PTT", 57: "V_PTT", 58: "V_PTT", 59: "Q/H0", 60: "Q/H1", 61: "信标机音+",
                        62: "信标机音-",
                        63: "屏蔽地", 64: "信标机音+", 65: "信标机音-", 66: "屏蔽地", 67: "主耳机出+", 68: "主耳机出-",
                        69: "屏蔽地",
                        70: "去VTR记录音+", 71: "去VTR记录音-", 72: "屏蔽地", 73: "备份控制设备串口2收+",
                        74: "备份控制设备串口2收-", 75: "屏蔽地", 76: "备份控制设备串口2发+",
                        77: "备份控制设备串口2发-", 78: "屏蔽地", 79: "启动信标机自检", 80: "信标过台信号",
                        81: "中央控制设备 PTT（M-PTT）", 82: "备份控制设备 PPT（M-PTT）", 83: "话台PTT出",
                        84: "去灯盒", 85: "灯测试信号", 86: "灯测试信号", 87: "中央控制设备故障",
                        88: "备份控制设备故障", 89: "M_D_PTT", 90: "V_D_PTT",
                        91: "D_PTT_O", 92: "产生告警音9", 93: "产生告警音10", 94: "产生告警音11", 95: "产生告警音14",
                        96: "产生告警音15", 97: "产生告警音16",
                        98: "产生告警音17", 99: "产生告警音18", 100: "产生告警音19", 101: "产生告警音20",
                        102: "产生告警音23", 103: "产生告警音24",
                        104: "产生告警音24", 105: "产生告警音25", 106: "产生告警音26", 107: "产生告警音27",
                        108: "产生告警音28", 109: "产生告警音31",
                        110: "产生告警音32", 111: "产生告警音33", 112: "产生告警音34", 113: "产生告警音35",
                        114: "产生告警音36", 115: "产生告警音37",
                        116: "产生告警音38", 117: "产生告警音39", 118: "+27V", 119: "供电指示", 120: "数台毁钥控制信号",
                        121: "数台毁钥控制信号",
                        122: "数台毁钥回线", 123: "产生告警音1", 124: "产生告警音2", 125: "产生告警音3",
                        126: "产生告警音4", 127: "产生告警音5",
                        128: "产生告警音7", 129: "产生告警音12", 130: "产生告警音13", 131: "产生告警音8",
                        132: "产生告警音21", 133: "产生告警音22",
                        134: "产生告警音29", 135: "产生告警音30", 136: "产生告警音6", 137: "产生告警音8",
                        138: "JIDS话音1收 +", 139: "JIDS话音 1收 -",
                        140: "屏蔽地", 141: "JIDS话音 1发 +", 142: "JIDS话音 1发 -", 143: "屏蔽地",
                        144: "JIDS话音 2发 +", 145: "JIDS话音2发 -",
                        146: "屏蔽地", 147: "JIDS话音1 PTT", 148: "JIDS话音2 PTT", 149: "电源控制", 150: "地",
                        151: "数台加电控制",
                        152: "数台加电回线", 153: "备份控制设备来收明话＋", 154: "备份控制设备来收明话－", 155: "屏蔽＋",
                        156: "去备份控制设备主机主耳机＋", 157: "去备份控制设备主机主耳机－",
                        158: "屏蔽", 159: "去备份控制设备发明话＋", 160: "去备份控制设备发明话－", 161: "屏蔽",
                        162: "去备份控制设备的VTR＋", 163: "去备份控制设备的VTR－",
                        164: "屏蔽", 165: "去备份控制设备副耳机＋", 166: "去备份控制设备副耳机－", 167: "屏蔽",
                        168: "串行口3收＋", 169: "串行口3收－",
                        170: "屏蔽地", 171: "串行口3发＋", 172: "串行口3发－", 173: "屏蔽地", 174: "睡眠指示＋",
                        175: "睡眠指示－",
                        176: "屏蔽", 177: "中央控制设备话台PPT出", 178: "中央控制设备数台PPT出", 179: "数台话台指示",
                        180: "＋12V电源", 181: "中央控制设备地",
                        182: "产生告警音1", 183: "产生告警音2", 184: "产生告警音3", 185: "产生告警音4",
                        186: "产生告警音5", 187: "产生告警音6",
                        188: "产生告警音7", 189: "产生告警音8", 190: "产生告警音8", 191: "产生告警音9",
                        192: "产生告警音12", 193: "产生告警音13",
                        194: "产生告警音21", 195: "产生告警音22", 196: "产生告警音29", 197: "产生告警音30",
                        198: "数台收明话＋", 199: "数台收明话－",
                        200: "屏蔽", 201: "数台发明话＋", 202: "数台发明话－", 203: "屏蔽", 204: "数台PPT出",
                        205: "中央控制设备加电控制",
                        206: "中央控制设备加电控制回线", 207: "备份控制设备加电控制", 208: "备份控制设备加电控制回线",
                        209: "中央控制设备 28V电源", 210: "中央控制设备 28V电源", 211: "中央控制设备 28V电源",
                        212: "28V电源", 213: "28V电源", 214: "28V电源", 215: "机载28V地", 216: "机载28V地",
                        217: "机载28V地",
                        218: "备份控制设备 28V电源", 219: "备份控制设备 28V电源", 220: "备份控制设备 28V电源",
                        221: "机载＋28V", 222: "机载＋28V", 223: "机载＋28V",
                        224: "机载28V地", 225: "机载28V地", 226: "机载28V地", 227: "+12V (前舱显控盒)",
                        228: "地(前舱显控盒)", 229: "+12V (后舱显控盒)",
                        230: "地(后舱显控盒)", 231: "三相交流", 232: "三相交流", 233: "三相交流", 234: "中线",
                        235: "115电源输入",
                        236: "电源输入", 237: "电源输入", 238: "电源输入（+）", 239: "电源输入（+）", 240: "电源输入（+）",
                        241: "电源输入（+）",
                        242: "电源输入（-）", 243: "电源输入（-）", 244: "电源输入（-）", 245: "电源输入（-）",
                        246: "电源输入（+）", 247: "电源输入（+）",
                        248: "电源输入（+）", 249: "电源输入（+）", 250: "注钥下传", 251: "电台注钥上传", 252: "地",
                        253: "副话筒入+",
                        254: "副话筒入-", 255: "屏蔽", 256: "主话筒入+", 257: "主话筒入-", 258: "屏蔽",
                        259: "备份来主话筒+",
                        260: "备份来主话筒-", 261: "屏蔽", 262: "备份来副话筒+", 263: "备份来副话筒-", 264: "屏蔽",
                        265: "后舱来话筒+",
                        266: "后舱来话筒-", 267: "屏蔽", 268: "前舱来话筒+", 269: "前舱来话筒-", 270: "屏蔽",
                        271: "备份来主话筒+",
                        272: "备份来主话筒-", 273: "备份来收明话+", 274: "备份来收明话-", 275: "JIDS收话音1＋",
                        276: "JIDS收话音1-", 277: "罗盘+",
                        278: "罗盘-", 279: "塔康导航音+", 280: "塔康导航音-", 281: "信标+", 282: "信标-",
                        283: "JIDS收话音2＋",
                        284: "JIDS收话音2-", 285: "导弹告警＋", 286: "导弹告警-", 287: "短波收＋", 288: "短波收-",
                        289: "MLS＋",
                        290: "MLS-", 291: "去备份主机主耳＋", 292: "去备份主机主耳-", 293: "短波电台发＋",
                        294: "短波电台发-", 295: "去备份发明话＋",
                        296: "去备份发明话-", 297: "去JIDS话音2＋", 298: "去JIDS话音2-", 299: "去JIDS话音1＋",
                        300: "去JIDS话音1-", 301: "去备份副耳机＋",
                        302: "去备份副耳机-", 303: "备份来副话筒＋", 304: "备份来副话筒-", 305: "CPU串口1收＋",
                        306: "CPU串口1收-", 307: "CPU串口2收＋",
                        308: "CPU串口2收-", 309: "串口3收＋", 310: "串口3收-", 311: "屏蔽", 312: "串口3发＋",
                        313: "串口3发-",
                        314: "屏蔽", 315: "睡眠指示＋", 316: "睡眠指示-", 317: "屏蔽", 318: "话台PTT出",
                        319: "中央设备数台PTT出",
                        320: "JIDSPTT出1", 321: "数/话指示", 322: "JIDSPTT出2", 323: "XGSE", 324: "IP1", 325: "IP5",
                        326: "+12V电源", 327: "+5V", 328: "CCU地", 329: "短波PTT出", 330: "信标机过台信号",
                        331: "地面电源指示",
                        332: "启动信标机BIT", 333: "话台收明话+", 334: "话台收明话-", 335: "后舱来话筒+",
                        336: "后舱来话筒-", 337: "数台收明话+",
                        338: "数台收明话-", 339: "话台发明话+", 340: "话台发明话-", 341: "数台发明话+",
                        342: "数台发明话-", 343: "去后舱耳机+",
                        344: "去后舱耳机-", 345: "前舱来话筒+", 346: "前舱来话筒-", 347: "罗盘+", 348: "罗盘-",
                        349: "信标+",
                        350: "信标-", 351: "去前舱耳机+", 352: "去前舱耳机-", 353: "MLS+", 354: "MLS-", 355: "串口4发+",
                        356: "串口4发-", 357: "屏蔽", 358: "串口2收+", 359: "串口2收-", 360: "屏蔽", 361: "串口2发+",
                        362: "串口2发-", 363: "屏蔽", 364: "串口4收+", 365: "串口4收-", 366: "屏蔽", 367: "话台PTT出",
                        368: "数台PTT出", 369: "+5V", 370: "去前舱+12V", 371: "去前舱地", 372: "去罗盘近台灯",
                        373: "供电指示", }

        # 遍历字典的键并返回值
        if channel_number in self.Chinese:
            return self.Chinese[channel_number]
        else:
            self.log.logger.error("11BS模块通道编号错误")
            return " "

    def __generate_color_map(self, n):
        """
        生成n个不同的颜色，用于标记不同的错误通道号。
        :param n: 需要的颜色数量
        :return: 包含PatternFill对象的颜色映射字典
        """
        colors = {}
        step = 1.0 / n
        for i in range(n):
            hue = i * step
            rgb = hsv_to_rgb(hue, 1.0, 1.0)
            color = Color(rgb=f"{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}")
            fill = PatternFill(start_color=color.rgb, end_color=color.rgb, fill_type='solid')
            colors[i] = fill
        return colors

    def _11bs_erro_info_save_excel(self, info: dict):
        """
        将错误列表的信息定位到文档中，并生成一个新的错误文档。
        :param info:错误列表
        :return:None
        """
        try:
            # 文件路径
            file_path = 'Doc/StandardForm/11BS测试定义查询表格.xlsx'
            # 输出文件路径
            output_path = 'Doc/ErroForm/11BS测试结果错误表格.xlsx'

            # 加载Excel工作簿
            wb = load_workbook(file_path)
            sheet = wb.active

            # 匹配列名
            column_to_match = "序号"

            # 错误字典
            values_to_style = info

            # 获取颜色字典
            color_map = self.__generate_color_map(len(values_to_style))

            # 默认填充样式
            default_fill = PatternFill(fill_type=None)

            # 获取“序号”列的索引
            header_row = next(sheet.iter_rows(min_row=2, max_row=3, values_only=True))
            try:
                column_index = header_row.index(column_to_match) + 1  # 索引从1开始
            except ValueError:
                self.log.logger.error(f"列名'{column_to_match}'未找到，请检查列名是否正确。")
                return None

            # 创建一个字典，用于存储“序号”到行号的映射
            number_to_row = {row[column_index - 1]: idx for idx, row in
                             enumerate(sheet.iter_rows(min_row=2, values_only=True), start=2)}

            # 清除所有单元格的现有填充样式，以避免样式叠加
            for row in sheet.iter_rows(min_row=2, max_col=sheet.max_column):
                for cell in row:
                    cell.fill = default_fill

            # 遍历错误字典中的每个键值对
            for i, (main_channel, connected_channels) in enumerate(values_to_style.items()):
                # 确定color_map中的键
                map_key = i % len(color_map)
                # 应用样式到主通道的行
                if main_channel in number_to_row:
                    row_num = number_to_row[main_channel]
                    # 更改B和C列的单元格样式
                    for col in sheet.iter_cols(min_col=2, max_col=3, min_row=row_num, max_row=row_num):
                        for cell in col:
                            cell.fill = color_map[map_key]

                # 应用样式到与主通道错误连接的其他通道的行
                for connected_channel in connected_channels:
                    if connected_channel in number_to_row:
                        row_num = number_to_row[connected_channel]
                        # 更改D和E列的单元格样式
                        for col in sheet.iter_cols(min_col=4, max_col=5, min_row=row_num, max_row=row_num):
                            for cell in col:
                                cell.fill = color_map[map_key]

            # 保存更改到原文件
            wb.save(output_path)
            wb.close()
            return True

        except Exception as e:
            self.log.logger.error(f"错误信息生成文档失败:{e}")
            return False
